package com.warrior.superdata.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
import com.warrior.superdata.base.BasePageResultVo;
import com.warrior.superdata.constants.CommonConstant;
import com.warrior.superdata.constants.ResultCode;
import com.warrior.superdata.dds.DataSourceHelp;
import com.warrior.superdata.enums.DatasourceType;
import com.warrior.superdata.enums.OperateType;
import com.warrior.superdata.handler.SqlHandler;
import com.warrior.superdata.mysql.entity.dto.ApiDatasourceDto;
import com.warrior.superdata.mysql.entity.dto.ApiInfoDetailDto;
import com.warrior.superdata.mysql.entity.dto.ApiResultDto;
import com.warrior.superdata.mysql.entity.dto.TableFieldDto;
import com.warrior.superdata.service.QuerySqlService;
import com.warrior.superdata.exceptionhandler.ApplicationException;
import com.warrior.superdata.util.PageUtil;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
import org.elasticsearch.client.*;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description:
 * @ClassName: EsQueryServiceImpl.java
 * @date: 2022/3/2 15:11
 * @author xujian
 */
@Service
@Slf4j
public class EsQueryServiceImpl implements QuerySqlService {

    private final static String ES_MAPPING_PROPERTIES = "properties";

    @Autowired
    private DataSourceHelp dataSourceHelp;

    @Autowired
    private SqlHandler sqlHandler;

    @Override
    public String getDatasourceType() {
        return DatasourceType.ES.getName();
    }

    @Override
    public String getOperateType() {
        return OperateType.QUERY.getName();
    }

    @Override
    public Object execute(ApiInfoDetailDto apiInfoDetailDto) throws ApplicationException {
        Object executeResult = null;

        String sql = sqlHandler.generateSql(apiInfoDetailDto);

        ApiDatasourceDto datasourceDto = apiInfoDetailDto.getDatasourceDto();
        if (null == datasourceDto) {
            throw new ApplicationException(ResultCode.FAIL_CODE, "数据源获取异常");
        }
        RestHighLevelClient restHighLevelClient = dataSourceHelp.getRestHighLevelClient(datasourceDto);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("query", sql);
        int fetchSize = 1;
        if (0 != apiInfoDetailDto.getResultType()) {
            fetchSize = 10000;
        }
        jsonObject.put("fetch_size", fetchSize);
        Request request = new Request("GET", "_sql");
        request.setJsonEntity(jsonObject.toJSONString());

        try {
            Response response = restHighLevelClient.getLowLevelClient().performRequest(request);
            InputStream inputStream = response.getEntity().getContent();
            String result = new BufferedReader(new InputStreamReader(inputStream))
                    .lines().collect(Collectors.joining(System.lineSeparator()));
            JSONObject apiResultObj = JSONObject.parseObject(result);

            JSONArray rows = apiResultObj.getJSONArray("rows");
            List<ApiResultDto> results = apiInfoDetailDto.getResults();
            if (CommonConstant.API_DATA_RESULT_ARR == apiInfoDetailDto.getResultType()) {
                JSONArray array = new JSONArray();
                rows.forEach(item -> {
                    JSONObject obj = handlerEsRow(item, results);
                    array.add(obj);
                });
                executeResult = array;
            } else if (CommonConstant.API_DATA_RESULT_PAGE == apiInfoDetailDto.getResultType()) {
                BasePageResultVo pageResultVo = new BasePageResultVo();
                JSONArray array = new JSONArray();
                rows.forEach(item -> {
                    JSONObject obj = handlerEsRow(item, results);
                    array.add(obj);
                });
                pageResultVo.setPage(apiInfoDetailDto.getPage());
                pageResultVo.setSize(apiInfoDetailDto.getSize());
                List page = PageUtil.page(array, apiInfoDetailDto.getPage(), apiInfoDetailDto.getSize());
                pageResultVo.setContent(page);
                executeResult = pageResultVo;
            } else if (CommonConstant.API_DATA_RESULT_SINGLE == apiInfoDetailDto.getResultType() && rows.size() > 0) {
                executeResult = handlerEsRow(rows.get(0), results);
            }
        } catch (Exception e) {
            log.error("es executeQuery执行异常 errMsg:{}", e.getMessage());
        }
        return executeResult;
    }

    private JSONObject handlerEsRow(Object item, List<ApiResultDto> results) {
        JSONObject obj = new JSONObject();
        for (int i = 0; i < results.size(); i++) {
            obj.put(results.get(i).getResultName(), ((JSONArray) item).get(i));
        }
        return obj;
    }

    @Override
    public List<String> testSourceConnect(ApiDatasourceDto apiDatasourceDto) {
        try {
            RestHighLevelClient restHighLevelClient = dataSourceHelp.getRestHighLevelClient(apiDatasourceDto, Boolean.FALSE);
            GetAliasesResponse alias = restHighLevelClient.indices().getAlias(new GetAliasesRequest(), RequestOptions.DEFAULT);
            System.out.println(alias);
        } catch (Exception e) {
            throw new ApplicationException(ResultCode.FAIL_CODE, "数据库连接异常 msg:" + e.getMessage());
        }
        return null;
    }

    @Override
    public Set<String> getTables(ApiDatasourceDto apiDatasourceDto) {
        Set<String> result = new LinkedHashSet<>();
        try {
            RestHighLevelClient restHighLevelClient = dataSourceHelp.getRestHighLevelClient(apiDatasourceDto);
            GetAliasesResponse alias = restHighLevelClient.indices().getAlias(new GetAliasesRequest(), RequestOptions.DEFAULT);
            Map<String, Set<AliasMetaData>> aliases = alias.getAliases();
            if (null != aliases && aliases.size() > 0) {
                aliases.keySet().forEach(key -> {
                    if (!CollectionUtils.isEmpty(aliases.get(key))) {
                        aliases.get(key).forEach(s -> {
                            result.add(s.alias());
                        });
                    } else {
                        result.add(key);
                    }
                });
            }
        } catch (Exception e) {
            log.error("获取表列表异常 errMsg:{}", e.getMessage());
        }
        return result;
    }

    @Override
    public List<TableFieldDto> getTableFields(ApiDatasourceDto apiDatasourceDto, String tableName) {
        List<TableFieldDto> result = new ArrayList<>();
        try {
            RestHighLevelClient restHighLevelClient = dataSourceHelp.getRestHighLevelClient(apiDatasourceDto);
            GetMappingsRequest getMappingsRequest = new GetMappingsRequest();
            getMappingsRequest.indices(tableName).types(new String[0]);
            IndicesClient index = restHighLevelClient.indices();
            GetMappingsResponse response = index.getMapping(getMappingsRequest, RequestOptions.DEFAULT);

            ImmutableOpenMap<String, ImmutableOpenMap<String, MappingMetaData>> mappingsByIndex = response.getMappings();
            for (ObjectObjectCursor<String, ImmutableOpenMap<String, MappingMetaData>> indexEntry : mappingsByIndex) {
                if (indexEntry.value.isEmpty()) {
                    continue;
                }
                log.info("es索引名称:{}", indexEntry.key);
                LinkedHashMap properties = (LinkedHashMap) indexEntry.value.get(CommonConstant.ES_DOC).getSourceAsMap().get(ES_MAPPING_PROPERTIES);
                properties.keySet().forEach(key -> {
                    TableFieldDto tableFieldDto = new TableFieldDto();
                    tableFieldDto.setFieldName(String.valueOf(key));
                    LinkedHashMap o = (LinkedHashMap) properties.get(key);
                    Object type = o.get("type");
                    if (ObjectUtil.isNotNull(type)) {
                        tableFieldDto.setFieldType(String.valueOf(type));
                    } else {
                        tableFieldDto.setFieldType(String.valueOf(properties.get(key)));
                    }
                    result.add(tableFieldDto);
                });
                break;
            }
        } catch (Exception e) {
            log.error("获取es表字段异常异常 errMsg:{}", e.getMessage());
        }
        return result;
    }


}
